// Copyright (c) 2009 - Quildreen <quildreen@gmail.com>                     
//                                                                          
//  Permission is hereby granted, free of charge, to any person              
//  obtaining a copy of this software and associated documentation files     
//  (the "Software"), to deal in the Software without restriction,           
//  including without limitation the rights to use, copy, modify, merge,     
//  publish, distribute, sublicense, and/or sell copies of the Software,     
//  and to permit persons to whom the Software is furnished to do so,        
//  subject to the following conditions:                                     
//                                                                           
//  The above copyright notice and this permission notice shall be           
//  included in all copies or substantial portions of the Software.          
//                                                                           
//  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,          
//  EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF       
//  MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND                    
//  NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE   
//  LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION   
//  OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION    
//  WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 

/**
 * lily.graphics.core
 *
 * Abstracts graphics rendering in javascript.
 **/
lily.defineModule("graphics.core");


/**
 * class lily.graphics.Surface
 * 
 * Dummy class with basic surface implementation abstraction that
 * should be overwrited by the specific classes in other modules.
 **/
lily.graphics.Surface = Class.create(
{
    /**
     * new lily.graphics.Surface([width = 0][, height = 0])
     * - width (Number): the width of the surface's canvas.
     * - height (Number): the height of the surface's canvas.
     **/
    initialize: function(width, height)
    {
        /**
         * lily.graphics.Surface#id -> Number (read only)
         * 
         * Unique identification for the instance.
         **/
        this.id = lily.uniqueID();

        /**
         * lily.graphics.Surface#_change_list -> Object (read only)
         * 
         * An object containing all the transformations to apply when
         * the surface is updated
         **/
        this._change_list = {};

        /**
         * lily.graphics.Surface#_childs -> Object
         * 
         * An object holding all childs of this surface, mapped by ID.
         **/
        this._childs = {};

        /**
         * lily.graphics.Surface#x -> Number
         * 
         * The horizontal position of this surface.
         **/
        this.x = 0;

        /**
         * lily.graphics.Surface#y -> Number
         * 
         * The vertical position of this surface.
         **/
        this.y = 0;

        /**
         * lily.graphics.Surface#z -> Number
         * 
         * The z-ordering of this surface.
         **/
        this.z = 0;

        /**
         * lily.graphics.Surface#width -> Number
         * 
         * The width of this surface.
         **/
        this.width = width || 0;

        /**
         * lily.graphics.Surface#height -> Number
         * 
         * The height of this surface.
         **/
        this.height = height || 0;

        /**
         * lily.graphics.Surface#angle -> Number
         * 
         * The current angle this surface is rotated.
         **/
        this.angle = 0;

        /**
         * lily.graphics.Surface#scale_x -> Number
         * 
         * The current scale factor of the horizontal part of this surface.
         **/
        this.scale_x = 1;

        /**
         * lily.graphics.Surface#scale_y -> Number
         * 
         * The current scale factor of the vertical part of this surface.
         **/
        this.scale_y = 1;

        /**
         * lily.graphics.Surface#opacity -> Number
         * 
         * The current opacity of this surface.
         **/
        this.opacity = 1;
    },


    /**
     * lily.graphics.Surface#attachTo(element) -> lily.graphics.Surface
     * - element (String | HTMLElement): the element where this
     * surface should be attached.
     * 
     * Attachs this surface to a HTMLElement, so it's visible on the page.
     **/
    attachTo: function(element)
    {
        $(element).insert(this._canvas);
        return this;
    },


    /**
     * lily.graphics.Surface#blit([surface][, position]) -> undefined
     * - surface (Surface): the surface to blit
     * - position (Array): optional `[x, y]` array.
     * 
     * Blits another surface into this one.
     * 
     * If `surface` is not given, we'll just update this Surface instead.
     * 
     * If `position` is not given, the position where to blit the
     * surface will be taken from the Surface instance.
     **/
    blit: function(surface, position)
    {
        /* void */
    },


    /**
     * lily.graphics.Surface#scroll([x = 0][, y = 0]) -> lily.graphics.Surface
     * 
     * Scrolls the contents of the surface.
     **/
    scroll: function(x, y)
    {
        return this;
    },


    /**
     * lily.graphics.Surface#fill(colour[, rect]) -> lily.graphics.Surface
     * - colour (String): CSS-style colour to use.
     * - rect (Object): optional rect of the are to fill.
     * 
     * Fills the surface (or a rectangle of this surface) with a solid colour.
     * 
     * The optional `rect` argument should be an object defining the
     * properties `left`, `right`, `top` and `bottom` (the boundaries
     * of the rectangle).
     * 
     * If a `rect` argument is not given, the entire surface will be filled.
     **/
    fill: function(colour, rect)
    {
        return this;
    },


    /**
     * lily.graphics.Surface#clear() -> lily.graphics.Surface
     * 
     * Clears the contents of the surface.
     **/
    clear: function()
    {
        return this;
    },


    /**
     * lily.graphics.Surface#load(url) -> lily.graphics.Surface
     * - url (String): url of the resource to load.
     * 
     * Loads an image or content from the given resource, and paints
     * it on the surface.
     **/
    load: function(url)
    {
        return this;
    },


    /**
     * lily.graphics.Surface#getStyle() -> String | null
     * 
     * Returns the CSS style for the surface.
     **/
    getStyle: function()
    {
        /* void */
    },


    /**
     * lily.graphics.Surface#setStyle(styles) -> lily.graphics.Surface
     * - styles (String | Object): the CSS rules to apply
     * 
     * Sets the CSS style for the surface.
     **/
    setStyle: function(styles)
    {
        return this;
    },


    /**
     * lily.graphics.Surface#rotate(angle) -> lily.graphics.Surface
     * - angle (Number): rotation angle, in degrees.
     * 
     * Applies 2d rotation to this surface. This will only cache the
     * operation and wait until a blit command is issued to apply all
     * the changes.
     **/
    rotate: function(angle)
    {
        this.angle = (this.angle + angle) % 360;

        this._change_list['rotation'] = true;
        return this;
    },


    /**
     * lily.graphics.Surface#scale(xfactor, yfactor) -> lily.graphics.Surface
     * - xfactor (Number): horizontal scale factor, as a float.
     * - yfactor (Number): vertical scale factor, as a float.
     * 
     * Scales the surface and its contents. This will only cache the
     * operation and wait until a blit command is issued to apply all
     * the changes.
     **/
    scale: function(xfactor, yfactor)
    {
        this.scale_x = xfactor;
        this.scale_y = yfactor;

        this._change_list['scale'] = true;
        return this;
    },


    /**
     * lily.graphics.Surface#setOpacity(opacity) -> lily.graphics.Surface
     * - opacity (Number): percentual of opacity, between 0 and 1.
     * 
     * Sets the opacity of the surface. This will only cache the
     * operation and wait until a blit command is issued to apply
     * all the changes.
     **/
    setOpacity: function(opacity)
    {
        this.opacity = opacity;

        this._change_list['opacity'] = true;
        return this;
    },


    /**
     * lily.graphics.Surface#setSize(width, height) -> lily.graphics.Surface
     * 
     * Sets the surface's size. This will only cache the operation and
     * wait until a blit command is issued to apply all the changes.
     **/
    setSize: function(width, height)
    {
        this.width = width;
        this.height = height;

        this._change_list['size'] = true;
        return this;
    }
});


/**
 * class lily.graphics.Display
 * 
 * Abstracts window and screen management.
 **/
lily.graphics.Display = Class.create(
{
    /**
     * new lily.graphics.Display(canvas[, size][, flags])
     * - canvas (String | HTMLElement): which element to render the screen.
     * - size (Array): size of the screen, as an `[width, height]` array.
     * - flags (Number): A list of flags for this display object.
     * 
     * Flags can be combined by using the bitwise or `|`.
     **/
    initialize: function(canvas, size, flags)
    {
        /**
         * lily.graphics.Surface#surface -> Surface
         * 
         * An instance of the Surface object used to represent the screen.
         **/
        this.surface = null;

        /**
         * lily.graphics.Surface#_canvas -> HTMLElement
         * 
         * The object to render the screen to.
         **/
        this._canvas = $(canvas);

        // if the given canvas doens't exists, create a new one and inserts in
        // the body.
        if (!this._canvas) {
            this._canvas = new Element("div");
            $$("body")[0].insert(this._canvas);
        }
    },


    /**
     * lily.graphics.Display#flip() -> undefined
     * 
     * Updates the entire screen.
     **/
    flip: function()
    {
        /* void */
    },


    /**
     * lily.graphics.Display#update(rects...) -> undefined
     * - rects (Object): positional rectangles of which parts of the
     *   screens to update. 
     * 
     * Updates only parts of the screen. It accepts a range of Rect objects
     * as positional arguments to denote which parts of the screen should be
     * updated.
     **/
    update: function()
    {
        /* void */
    }
});